#include "Constants.h"
#include "Sphere.h"
#include <math.h>

using namespace std;

Sphere::Sphere(double x, double y, double z, double r, Shader* s) {
    center.set(x, y, z);
    radius = r;
    shader = s;
    bbox = Box(x - r, x + r, y - r, y + r, z - r, z + r);
}

bool Sphere::isHit(Ray& r, double tmin, double tmax, Record& rec) {
    double discriminate = 0.0;
    double t1, t2, front, under;
    cs5721::Vector3D negRDirection, rOrigin, rDirection, oMinusC;

    rOrigin = r.getOrigin();
    rDirection = r.getDirection();
    negRDirection = rDirection * -1.0;
    oMinusC = rOrigin - center;
    double discTemp1 = pow(rDirection.dot(oMinusC), 2.0);
    double d1 = oMinusC.dot(oMinusC);
    double discTemp2 = (d1 - (radius * radius));

    discriminate = discTemp1 - ((rDirection.dot(rDirection)) * (discTemp2));

    if (discriminate < 0.0) {
        return false;
    } else {
        under = rDirection.dot(rDirection);
        
        front = negRDirection.dot(oMinusC);

        if (discriminate < eps && discriminate > -eps) {
            rec.t = front / under;
            if(rec.t < tmin || rec.t > tmax) {
                return false;
            }
            cs5721::Vector3D norm = rOrigin + (rDirection * rec.t);
            rec.shader = shader;
            rec.normal = getNormal(norm);
            //cout << rec.normal << endl;
            return true;
        } else {
            t1 = (front + sqrt(discriminate)) / under;
            t2 = (front - sqrt(discriminate)) / under;
            rec.t = (t1 < t2) ? t1 : t2;
            if(rec.t < tmin || rec.t > tmax) {
                return false;
            }
            cs5721::Vector3D norm = rOrigin + (rDirection * rec.t);
            rec.shader = shader;
            rec.normal = getNormal(norm);
            //cout << rec.normal << endl;
            return true;
        }
    }
}

cs5721::Vector3D Sphere::getNormal(cs5721::Vector3D& pointHit) {
    cs5721::Vector3D normal;
    normal = pointHit - center;
    normal.normalize();
    return normal;
}


Box Sphere::boundingBox() {
    return bbox;
}
Shader* Sphere::getShader() {
    return shader;
}

cs5721::Vector3D Sphere::getColor() {
    return shader->getSurfaceColor();
}

void Sphere::print() {
    cout << "Sphere: ";
    bbox.print();
}